package com.gy.common.util;

import ch.obermuhlner.math.big.BigDecimalMath;
import com.google.common.collect.Lists;
import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 总体负荷预测
 */
public class BigDemicalUtils {
    //指数
    public static BigDecimal[] exp_linest(List<BigDecimal> y, List<BigDecimal> x) {
        List<BigDecimal> yy = Lists.newArrayList();
        MathContext decimal64 = MathContext.DECIMAL64;
        for (BigDecimal bigDecimal : y) {
            yy.add(BigDecimalMath.log(bigDecimal, decimal64));
        }
        BigDecimal[] _linest = linest(yy, x);
        BigDecimal a = BigDecimalMath.pow(BigDecimalMath.e(decimal64), _linest[1], decimal64);
        BigDecimal b = _linest[0];
        return new BigDecimal[]{a, b};
    }

    //一元线性回归(线性)
    public static BigDecimal[] linest(List<BigDecimal> _y, List<BigDecimal> _x) {
        OLSMultipleLinearRegression oregression = new OLSMultipleLinearRegression();

//        String[] yList =
//                new String[]{
//                        "4.42", "5.42", "", "", "", "", "", "", "", "", ""};
//        String[] xList =
//                new String[]{
//                        "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"};


        double[] x = new double[_x.size()];
        double[][] y = new double[_y.size()][1];

        for (int i = 0; i < _y.size(); i++) {
            x[i] = _y.get(i).doubleValue();
            y[i] = new double[]{_x.get(i).doubleValue()};
        }
        oregression.newSampleData(x, y);

        double[] beta = oregression.estimateRegressionParameters();

        return new BigDecimal[]{BigDecimal.valueOf(beta[1]), BigDecimal.valueOf(beta[0])};
//        BigDecimal x_ = x.stream().
//                reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(x.size()), decimal64);
//        BigDecimal y_ = y.stream().
//                reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(y.size()), decimal64);
//        // 平方和
//        List<BigDecimal> multiplySum = Lists.newArrayList();
//        for (int i = 0; i < y.size(); i++) {
//            multiplySum.add(y.get(i).multiply(x.get(i)));
//        }
//
//        BigDecimal xy_ = multiplySum.stream().
//                reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(multiplySum.size()), decimal64);
//
//        List<BigDecimal> power = Lists.newArrayList();
//        for (int i = 0; i < x.size(); i++) {
//            power.add(BigDecimalMath.pow(x.get(i), 2, decimal64));
//        }
//        BigDecimal x2_ = power.stream().
//                reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(power.size()), decimal64);
//        BigDecimal a = x_.multiply(y_).subtract(xy_).divide(BigDecimalMath.pow(x_, 2, decimal64).subtract(x2_), decimal64);
//        BigDecimal b = y_.subtract(a.multiply(x_));

    }


    //线性2
    private static BigDecimal[] linest2(List<BigDecimal> yNum, List<BigDecimal> xNum) {
        MathContext decimal64 = MathContext.DECIMAL64;
        BigDecimal xa = xNum.stream().
                reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(xNum.size()), decimal64);
        BigDecimal ya = yNum.stream().
                reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(yNum.size()), decimal64);
        BigDecimal Lxx = BigDecimal.ZERO;
        BigDecimal Lxy = BigDecimal.ZERO;
        for (int i = 0; i < xNum.size(); i++) {
            BigDecimal subtract = xNum.get(i).subtract(xa);
            Lxx = Lxx.add(subtract.multiply(subtract));
            Lxy = Lxy.add(subtract.multiply(yNum.get(i).subtract(ya)));
        }
        BigDecimal a = Lxy.divide(Lxx, decimal64);
        BigDecimal b = ya.subtract(a.multiply(xa));
        return new BigDecimal[]{a, b};
    }

    //幂函数
    public static BigDecimal[] pow_linest(List<BigDecimal> y, List<BigDecimal> x) {
        MathContext decimal64 = MathContext.DECIMAL64;

        List<BigDecimal> yy = y.stream().map(e -> BigDecimalMath.log(e, decimal64)).collect(Collectors.toList());
        List<BigDecimal> xx = x.stream().map(e -> BigDecimalMath.log(e, decimal64)).collect(Collectors.toList());
        BigDecimal[] _linest = linest(yy, xx);
        BigDecimal a = BigDecimalMath.pow(BigDecimalMath.e(decimal64), _linest[1], decimal64);
        BigDecimal b = _linest[0];
        return new BigDecimal[]{a, b};
    }

    //对数
    public static BigDecimal[] log_linest(List<BigDecimal> y, List<BigDecimal> x) {
        MathContext decimal64 = MathContext.DECIMAL64;
        List<BigDecimal> yy = y;
        List<BigDecimal> xx = x.stream().map(e -> BigDecimalMath.log(e, decimal64)).collect(Collectors.toList());
        BigDecimal[] _linest = linest(yy, xx);
        BigDecimal a = _linest[0];
        BigDecimal b = _linest[1];
        return new BigDecimal[]{a, b};
    }


    /**
     * excel计算power
     *
     * @return
     */
    public static BigDecimal pow(BigDecimal x, BigDecimal y, MathContext mathContext) {
        BigDecimal pow;
        if (x.compareTo(BigDecimal.ZERO) < 0) {
            pow = BigDecimalMath.pow(x.abs(MathContext.DECIMAL64), y, mathContext).multiply(new BigDecimal(-1));
        } else {
            pow = BigDecimalMath.pow(x, y, mathContext);
        }
        return pow;
    }


    /**
     * 获取年份对应字段
     *
     * @param start 开始年份
     * @param now   当前年份
     * @param end   结束年份
     */
    public static List<Yearkey> getYearKey(int start, int now, int end) {
        int i = start % 5;
        int firstFivePlan = start + (5 - (i == 0 ? 5 : i)) + 5;
        int i3 = 0;
        List<Yearkey> list = new ArrayList<>();
        for (int i2 = start; i2 <= end; i2++) {
            boolean before = (now - i2) > 0;
            String i4 = before ? "n_sub" + (now - i2) : "n_add" + (i2 - now);
            list.add(Yearkey.builder().field(i4).label(Integer.toString(i2)).type("0").before(before).build());
            if (i2 % 5 == 0 && i2 >= firstFivePlan) {
                int i1 = (i2 - 2005) / 5 + 10;
                i3++;
                list.add(Yearkey.builder().field("f_" + i3).label(numToChniese(i1).replace("一十", "十") + "五计划").type("1")
                        .start(Integer.toString(i2 - 5))
                        .end(Integer.toString(i2))
                        .before(before).build());
            }
        }
        return list;
    }


    private void t() {


        // 1.第一个百分比计算  2.修改历史记录 ，对修改当期的影响 3.提交后不能修改 4.根据是否提交 重新计算？ 5.所以 我必须把计算提成方法

        String[] yList =
                new String[]{
                        "4.42", "5.42", "", "", "", "", "", "", "", "", ""};
        String[] xList =
                new String[]{
                        "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"};


//        List<BigDecimal> y = Lists.newArrayList();
//        List<BigDecimal> x = Lists.newArrayList();
//        for (String s : yList) {
//            y.add(new BigDecimal(s));
//        }
//
//        for (String s : xList) {
//            x.add(new BigDecimal(s));
//        }
//
//        BigDecimal[] exp = log_linest(y, x);
//        System.out.println("指数" + exp[0] + ";" + exp[1]);

//        double[] y = new double[]{4.42, 5.42, 7.34, 8.37, 10.03, 12.51, 14.64, 17.30, 19.97, 22.72, 25.34};
//        double[] v = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
//        t(y, v);


        int start = 2005;
        int end = 2030;
        int now = 2016;

        // 2000 第十个 五年计划

        // 计算五年计划

        int i = start % 5;


//        int i1 = (第一个五年计划 - 2005) / 5 + 10;  2001 2002 2003 2004 2005   》》 2003 ---> 我就需要尝试找到 2001 没有就放弃第一个五年计划


        //1.根据开始时间 确定五年计划时间 确定前一个五年开始 可不可以 2005 开始时间
        int i5 = start - i + 1;
        boolean flag = false;
        for (int i1 = i5; i1 < start; i1++) {
            //TODO 都存在 开始时间确定 添加个判断。。。
            flag = true;
        }
        int firstFivePlan;
        if (!flag) {
            // 不都存在 需要使用下一个五年计划开始 第一个五年计划结尾
            firstFivePlan = start + (5 - (i == 0 ? 5 : i)) + 5;
        } else {
            firstFivePlan = start + (5 - (i == 0 ? 5 : i));
        }


        // 计算 方式 ----- 2021 年 然后值添加到固定位置 怎么处理？？？？ 2021--然后添加---


        // 添加流程 1.   2.   3.   4.

        Map<Object, Object> map = new HashMap<>();
        int i3 = 0;
        for (int i2 = start; i2 <= end; i2++) {
            String i4 = (now - i2) >= 0 ? "n_sub_" + (now - i2) : "n_add_" + (i2 - now);
            if (i2 < firstFivePlan) {
                // TODO 按年查询历史数据 或者计算新数据 然后再说
                System.out.println(i4);
                System.out.println(i2);
            } else {
                System.out.println(i4);
                System.out.println(i2);
                if (i2 % 5 == 0) {
                    int i1 = (i2 - 2005) / 5 + 10;

                    String s = numToChniese(i1).replace("一十", "十");
                    i3++;
                    System.out.println("f_" + i3);
                    System.out.println(s);
                }
            }
        }
    }

    static String[] valToChn = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
    static String[] sectionUnit = {"", "万", "亿", "万亿"};


    public static String numToChniese(int num) {
        String res = "";
        int index = 0; //等于0 可以表示这是右边第一个section，用于判定是否将 0000 转换为零
        while (num > 0) {
            int section = num % 10000;
            num = num / 10000;
            if (num == 0) { //num == 0表示 这是最后一个part 那么这个part中的前导0不能变成零 如 0010 应该为 一十
                String temp = sectionToChniese(section, true);
                if (temp == null && index != 0) temp += valToChn[0];
                res = temp + sectionUnit[index] + res;
            } else {  //非最后一个part 前导0可以变成零 如 1 0001 为：一万零一
                String temp = sectionToChniese(section, false);
                if (temp == null && index != 0)
                    temp += valToChn[0]; //整个section都为0的情况：如果当前section是最右侧的section就不用转换为零，如 1 0000 一万
                res = temp + sectionUnit[index] + res;
            }
            index++;  //section的权重上升
        }
        return res;
    }

    //辅助函数：将4位的section转换为中文，对于0000是会返回 null，因此 0000需要主逻辑特判
    private static String sectionToChniese(int section, boolean isFirstSection) {
        StringBuilder sb = new StringBuilder();

        int qian = (section / 1000) % 10;  //取出千位
        int bai = (section / 100) % 10;
        int shi = (section / 10) % 10;
        int ge = (section / 1) % 10;

        if (qian != 0) {
            sb.append(valToChn[qian]).append("千");

        } else if (isFirstSection || (bai == 0 && shi == 0 && ge == 0)) { //不加零的情况： 前面一位是0 或者 后面所有位为0

        } else {
            sb.append(valToChn[0]); //0不用添加单位
        }

        if (bai != 0) {
            sb.append(valToChn[bai]).append("百");
        } else if (bai == 0 && (qian == 0 || (ge == 0 && shi == 0))) {  //不加零的情况： 前面一位是0 或者 后面所有位为0

        } else {
            sb.append(valToChn[0]);
        }
        if (shi != 0) {
            sb.append(valToChn[shi]).append("十");
        } else if (shi == 0 && (bai == 0 || (ge == 0))) {  //不加零的情况： 前面一位是0 或者 后面所有位为0

        } else {
            sb.append(valToChn[0]);
        }

        if (ge != 0) {  //个位为0直接不用翻译了
            sb.append(valToChn[ge]);
        }

        return sb.toString();
    }


    //多项式 Y=ax2+bx+c
    public static BigDecimal[] t(List<BigDecimal> _y, List<BigDecimal> _x) {
        double[] y = new double[_y.size()];
        double[] v = new double[_x.size()];

        for (int i = 0; i < _y.size(); i++) {
            y[i] = _y.get(i).doubleValue();
        }
        for (int i = 0; i < _x.size(); i++) {
            v[i] = _x.get(i).doubleValue();
        }


        OLSMultipleLinearRegression oregression = new OLSMultipleLinearRegression();

        double[][] x = new double[y.length][2];
        double[][] x2 = new double[y.length][2];

        for (int i = 0; i < v.length; i++) {
            x2[i] = new double[]{v[i]};
            x[i] = new double[]{v[i], Math.pow(v[i], 2)};
        }

        oregression.newSampleData(y, x);

        double[] beta = oregression.estimateRegressionParameters();

        System.out.println("---多元回归参数---");


        // beta[0] c值 beta[1] b值 beta[2] a值
        for (double d : beta) {
            System.out.print(d);
            System.out.print("  ");
        }

        return new BigDecimal[]{
                new BigDecimal(beta[2]), new BigDecimal(beta[1]), new BigDecimal(beta[0])
        };
//        double xx = 2016 - 2005 + 1;
//        double pY = beta[2] * Math.pow(xx, 2) + beta[1] * xx + beta[0];
//
//        // a b c
//
//        System.out.println();
//        System.out.println("y=ax2+bx+c " + "x=" + xx + "  " + "y=" + pY);
//
//        oregression.newSampleData(y, x2);
//
//        double[] beta2 = oregression.estimateRegressionParameters();
//
//        System.out.println();
//        System.out.println("---线性回归参数---");
//
//        for (double d : beta2) {
//            System.out.print(d);
//            System.out.print("  ");
//        }
    }

}



